/************************************************************************************
**  
*    @copyright (c) 2013-2100,  Technology Co., LTD. All Right Reserved.
*
************************************************************************************/
/**
* @file	    duye_ini.cpp
* @version     
* @brief      
* @author
* @date	    2013-11-15
* @note 
*
*  1. 2013-11-15 Created this file
* 
*/
#include <duye_helper.h>
#include <duye_ini_section.h>
#include <duye_ini.h>

namespace duye {

static const int8* DUYE_LOG_PREFIX = "duye.ini";
static const uint64 INI_TMP_BUF_SIZE = 1024 * 10;

IniFile::IniFile() {
	m_error.setPrefix(DUYE_LOG_PREFIX);
}

IniFile::IniFile(const std::string& filePath) {
	m_error.setPrefix(DUYE_LOG_PREFIX);
    loadFile(m_filePath);
}

IniFile::~IniFile() {
}

bool IniFile::loadFile(const std::string& filePath) {
    m_filePath = filePath;
    
    cleanSectionMap();

    File file(m_filePath.c_str());
    if (!file.open(OPEN_RDWR)) {
    	ERROR_DUYE_LOG("%s", file.error());
    	return false;
    }

    int64 fileSize = file.size();
    if (fileSize <= 0) {
    	// file.close();
    	// ERROR_DUYE_LOG("file is empty");
    	return true;
    }

    int8* buffer = new int8[fileSize + 1];
    int64 readSize = file.read(buffer, fileSize);
    file.close();



    if (readSize != fileSize) {
    	ERROR_DUYE_LOG("read file failed");
    	return false;    
    }

    buffer[readSize] = 0;
    bool ret = importData(buffer, fileSize);

    delete [] buffer;
    buffer = NULL;

    return ret;
}

bool IniFile::importData(const std::string& fileData) {
    return importData(fileData.c_str(), fileData.length());
}

bool IniFile::importData(const int8* data, const uint64 length) {
    std::list<std::string> lineList; 
    StrHelper::getLine(data, "\n", lineList);
    IniSection* section = NULL;
    std::string sec_name;

    std::list<std::string>::iterator iter = lineList.begin();
    for (; iter != lineList.end(); ++iter) {
        std::string key;
        std::string value;
        std::string line = *iter;
        // printf("line >> %s \n", line.c_str());
        if (line[0] == '[') {
            size_t end_pos = line.find("]");
            if (end_pos == std::string::npos) {
                continue;
            }

            if (section) {
                m_iniSectionMap.insert(std::make_pair(sec_name, section));
            }            

            sec_name = line.substr(1, end_pos - 1);
            // printf("section = %s \n", sec_name.c_str());

            section = new IniSection(sec_name);
        } else {
            std::vector<std::string> key_and_value;
            StrHelper::split(line, '=', key_and_value);
            if (key_and_value.empty()) {
                continue;
            }

            key = key_and_value[0];
            value = key_and_value[1];
            // printf("key=%s value=%s \n", key.c_str(), value.c_str());

            if (section) {
                section->addPara(key, value);
            }
        }
    }

    if (section->getkeyValueMap().empty()) {
        delete section;
        section = NULL;
    } else {
        m_iniSectionMap.insert(std::make_pair(sec_name, section)); 
    }

    return true;
}

bool IniFile::getParaVal(const std::string& section, const std::string& paraName, std::string& value) {
    AutoLock autoLock(m_mapMutex); 
    IniSectionMap::iterator iter = m_iniSectionMap.find(section);
    if (iter == m_iniSectionMap.end()) {
    	ERROR_DUYE_LOG("section failed");
    	return false;
    }

    return iter->second->getPara(paraName, value);
}

bool IniFile::setParaVal(const std::string& section, const std::string& paraName, const std::string& value) {
    AutoLock autoLock(m_mapMutex); 
    IniSectionMap::iterator iter = m_iniSectionMap.find(section);
    if (iter == m_iniSectionMap.end()) {
        IniSection* section_node = new IniSection(section);
        section_node->addPara(paraName, value);
        m_iniSectionMap.insert(std::make_pair(section, section_node));
        return true;
    }

    return iter->second->setPara(paraName, value);
}

bool IniFile::delSection(const std::string& section) {
    AutoLock autoLock(m_mapMutex); 
    IniSectionMap::iterator iter = m_iniSectionMap.find(section);
    if (iter == m_iniSectionMap.end()) {
    	return false;
    }

    m_iniSectionMap.erase(iter);
    return true;    
}

bool IniFile::delPara(const std::string& section, const std::string& paraName) {
    AutoLock autoLock(m_mapMutex); 
    IniSectionMap::iterator iter = m_iniSectionMap.find(section);
    if (iter == m_iniSectionMap.end()) {
    	return false;
    }

    return iter->second->delPara(paraName);     
}

bool IniFile::saveFile() {
    if (m_filePath.empty()) {
    	return false;
    }

    saveFile(m_filePath);
    return true;
}

bool IniFile::saveFile(const std::string& filePath) {
    if (filePath.empty()) {
    	return false;
    }

    int8 tmpBuf[INI_TMP_BUF_SIZE] = {0};
    uint64 bufPos = 0;

    AutoLock autoLock(m_mapMutex);

    IniSectionMap::iterator iter = m_iniSectionMap.begin();
    for (; iter != m_iniSectionMap.end(); ++iter) {
    	bufPos += sprintf(tmpBuf + bufPos, "[%s]\n", iter->first.c_str());
    	if (bufPos >= INI_TMP_BUF_SIZE) {
    	    return false;
    	}       

        IniSection* section = iter->second;
        const IniSection::KeyValueMap& keyValueMap = section->getkeyValueMap();
		IniSection::KeyValueMap::const_iterator iter = keyValueMap.begin();
		for (; iter != keyValueMap.end(); ++iter) {
		    bufPos += sprintf(tmpBuf + bufPos, "%s=%s\n", iter->first.c_str(), iter->second.c_str()); 
		    if (bufPos >= INI_TMP_BUF_SIZE) {
		    	return false;
		    }
        }
    }

    tmpBuf[bufPos] = 0;
    File file(m_filePath.c_str());
    if (file.open(OPEN_WRITE) != true) {
    	return false;
    }    

    uint64 writeBytes = file.write(tmpBuf, bufPos);
    file.close();

    if (writeBytes != bufPos) {
    	return false;   
    }

    return true;
}

uint8* IniFile::error() {
    return m_error.error;
}

void IniFile::cleanSectionMap() {
    AutoLock autoLock(m_mapMutex); 
    IniSectionMap::iterator iter = m_iniSectionMap.begin();
    while (iter != m_iniSectionMap.end()) {
    	delete iter->second;
    	m_iniSectionMap.erase(iter++);   
    }
}

}
